package com.ruralflow.system.service.impl;

import com.ruralflow.system.service.IActivitiService;
import lombok.extern.slf4j.Slf4j;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.InputStream;
import java.util.List;
import java.util.Map;

/**
 * @author bairimengchang
 * @version 1.0
 * @description 流程引擎业务接口实现类（对activiti流程引擎提供的api接口进行二次封装）
 * @date 2025/1/18
 */
@Service
@Slf4j
public class ActivitiServiceImpl implements IActivitiService {

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private HistoryService historyService;


    private static final String BPMN_FILE_EXTENSION = ".bpmn20.xml";

    /**
     * 部署流程
     * @param inputStream bpmn文件流
     * @param processName 流程名称
     */
    @Override
    public ProcessDefinition deployProcess(InputStream inputStream, String processName) {
        log.info("尝试部署流程，流程名称：{}", processName);
        // 流程部署
        Deployment deployment = repositoryService.createDeployment()
                .addInputStream(BPMN_FILE_EXTENSION, inputStream)
                .name(processName)
                .deploy();

        // act_re_procdef 可以看这个表中有没有部署记录
        log.info("流程部署成功，流程部署ID：{}", deployment.getId());
        // 立刻查询流程部署信息并返回
        return repositoryService.createProcessDefinitionQuery()
                .deploymentId(deployment.getId())
                .singleResult();
    }

    /**
     * 删除流程（非强制删除，如果有关联流程示例则会报错）
     * @param processDefinitionId 流程定义ID
     */
    @Override
    public void deleteProcess(String processDefinitionId) {
        log.info("尝试删除流程定义id：{}", processDefinitionId);
        // 调用activiti的api方法尝试删除掉流程定义（有关联流程则会抛出异常）
        repositoryService.deleteDeployment(processDefinitionId);
        log.info("删除流程定义id：{}成功", processDefinitionId);
    }

    /**
     * 强制删除流程
     * @param processDefinitionId 流程定义ID
     */
    @Override
    public void forceDeleteProcess(String processDefinitionId) {
        log.info("强制删除流程定义id：{}", processDefinitionId);
        repositoryService.deleteDeployment(processDefinitionId, true);
        log.info("强制删除流程定义id：{}成功", processDefinitionId);
    }

    /**
     * 启动流程实例
     * @param processDefinitionKey 流程定义key
     * @param businessKey 业务主键
     * @param variables 流程变量（可以什么参数都不携带new HashMap<>()即可，直接在bpmn文件写死也可以）
     */
    @Override
    public ProcessInstance startProcessInstance(String processDefinitionKey,
                                                String businessKey,
                                                Map<String, Object> variables) {
        log.info("尝试启动，流程定义key：{}", processDefinitionKey);
        return runtimeService.startProcessInstanceByKey(processDefinitionKey, businessKey, variables);
    }

    /**
     * 查询指定处理人的待办任务列表
     * @param assignee 处理人
     * @return List<Task> 待办任务列表
     */
    @Override
    public List<Task> findTaskList(String assignee) {
        log.info("查询 {} 的待办任务列表", assignee);
        return taskService.createTaskQuery()
                .taskAssignee(assignee)
                .list();
    }

    /**
     * 查询当前流程实例的待办任务（目前只支持单流程对应单人待办查询）
     * @param processInstanceId 流程实例ID，可以从业务表中绑定的记录字段直接取到
     * @return String 待办任务处理人
     */
    @Override
    public Task findTask(String processInstanceId) {
        log.info("查询流程实例 {} 的待办任务", processInstanceId);
        return taskService.createTaskQuery()
                .processInstanceId(processInstanceId)
                .singleResult();
    }

    /**
     * 完成当前任务（即正常完成（同意申请）当前节点任务，往下继续任务流程，任务id可以通过上面的方法取到任务，再取id即可）
     * @param taskId 任务ID
     * @param variables 流程变量（可以什么参数都不携带new HashMap<>()即可）
     */
    @Override
    public void completeTask(String taskId, Map<String, Object> variables) {
        log.info("完成任务，任务ID：{}", taskId);
        taskService.complete(taskId, variables);
    }

    /**
     * 终止任务流程（即不同意审批，要驳回）
     * @param processInstanceId 流程实例ID，可以从业务表中绑定的记录字段直接取到
     */
    @Override
    public void terminateProcessInstance(String processInstanceId) {
        log.info("终止流程实例，流程实例ID：{}", processInstanceId);
        // 结束流程实例
        runtimeService.deleteProcessInstance(processInstanceId, "任务被驳回，流程被迫终止");
        // 记录日志或其他后续操作
        log.info("流程实例 {} 已结束", processInstanceId);
    }

    /**
     * 查询已经完成的任务列表
     * @param assignee 处理人
     * @return List<HistoricTaskInstance> 已经完成的任务列表
     */
    @Override
    public List<HistoricTaskInstance> findProcessedHistoricTaskList(String assignee) {
        return historyService.createHistoricTaskInstanceQuery()
                .taskAssignee(assignee)
                .finished()
                .list();
    }

    /**
     * 根据流程id获取业务表关联businessKey
     * @param processInstanceId 流程实例ID，可以从业务表中绑定的记录字段直接取到
     * 业务表中的关联字段，即businessKey
     */
    @Override
    public String getBusinessKeyByProcessInstanceId(String processInstanceId) {
        // 通过流程id获取业务键
        return historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult()
                .getBusinessKey();
    }

}
